﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using System.Windows.Forms;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Xna.Graphics.Implementation;
using Tesla.Xna.Util;
using XFG = Microsoft.Xna.Framework.Graphics;
using XNA = Microsoft.Xna.Framework;

namespace Tesla.Xna.Graphics {
    public sealed class XNARenderer : BaseRenderer {
        private XFG.GraphicsDevice _device;
        private Form _dummyForm;

        private Viewport _viewPort;
        private Rectangle _scissorRectangle;
        private Color _blendFactor;
        private int _blendSampleMask;
        private int _depthStencilReference;

        public XFG.GraphicsDevice GraphicsDevice {
            get {
                return _device;
            }
        }

        public override Viewport Viewport {
            get {
                return _viewPort;
            }
            set {
                _viewPort = value;
                XFG.Viewport vp;
                XNAHelper.ConvertViewport(ref _viewPort, out vp);
                _device.Viewport = vp;
            }
        }

        public override Rectangle ScissorRectangle {
            get {
                return _scissorRectangle;
            }
            set {
                _scissorRectangle = value;
                XNA.Rectangle rect;
                XNAHelper.ConvertRectangle(ref _scissorRectangle, out rect);
                _device.ScissorRectangle = rect;
            }
        }

        public override Color BlendFactor {
            get {
                return _blendFactor;
            }
            set {
                _blendFactor = value;
                XNA.Color c;
                XNAHelper.ConvertColor(ref _blendFactor, out c);
                _device.BlendFactor = c;
            }
        }

        public override int MultiSampleMask {
            get {
                return _blendSampleMask;
            }
            set {
                _blendSampleMask = value;
                // ??
            }
        }

        public override int ReferenceStencil {
            get {
                return _depthStencilReference;
            }
            set {
                _depthStencilReference = value;
                _device.ReferenceStencil = _depthStencilReference;
            }
        }

        public override ISamplerStateCollection SamplerStates {
            get { throw new NotImplementedException(); }
        }

        public override ISamplerStateCollection VertexSamplerStates {
            get { throw new NotImplementedException(); }
        }

        public override ITextureCollection Textures {
            get { throw new NotImplementedException(); }
        }

        public override ITextureCollection VertexTextures {
            get { throw new NotImplementedException(); }
        }

        public override IGraphicsAdapter Adapter {
            get { throw new NotImplementedException(); }
        }

        internal XNARenderer(XFG.GraphicsProfile profile) {
            _dummyForm = new System.Windows.Forms.Form();
            XFG.PresentationParameters pp = new XFG.PresentationParameters();
            pp.BackBufferFormat = XFG.SurfaceFormat.Color;
            pp.BackBufferHeight = 1;
            pp.BackBufferWidth = 1;
            pp.DepthStencilFormat = XFG.DepthFormat.None;
            pp.DeviceWindowHandle = _dummyForm.Handle;
            pp.PresentationInterval = XFG.PresentInterval.Immediate;
            pp.IsFullScreen = false;
            _device = new XFG.GraphicsDevice(XFG.GraphicsAdapter.DefaultAdapter, profile, pp);
        }

        internal XNARenderer(XFG.GraphicsDevice device) {
            _device = device;
        }

        public override void Clear(ClearOptions options, Color color, float depth, int stencil) {
            throw new NotImplementedException();
        }

        public override void SetRenderTarget(RenderTarget2D renderTarget) {
            throw new NotImplementedException();
        }

        public override void SetRenderTarget(RenderTargetCube renderTarget, CubeMapFace cubeMapFace) {
            throw new NotImplementedException();
        }

        public override void SetRenderTargets(params RenderTargetBinding[] renderTargets) {
            throw new NotImplementedException();
        }

        public override void SetVertexBuffer(VertexBuffer vb) {
            _device.SetVertexBuffer(((XNAVertexBufferImplementation) vb.Implementation).XNAVertexBuffer);
        }

        public override void SetVertexBuffer(VertexBuffer vertexBuffer, int vertexOffset) {
            throw new NotImplementedException();
        }

        public override void SetVertexBuffers(params VertexBufferBinding[] vertexBuffers) {
            throw new NotImplementedException();
        }


        public override void SetIndexBuffer(IndexBuffer ib) {
            _device.Indices = ((XNAIndexBufferImplementation) ib.Implementation).XNAIndexBuffer;
        }

        public override void Draw(PrimitiveType primType, int vertexCount, int startVertex) {
            _device.DrawPrimitives(XNAHelper.ToXNAPrimitiveType(primType), startVertex, XNAHelper.GetPrimitiveCount(primType, vertexCount, false, 0));
        }

        public override void DrawIndexed(PrimitiveType primType, int indexCount, int startIndex, int baseVertex) {
            XFG.PrimitiveType xnaPrim;
            int numPrims;
            ComputeXNADrawCall(primType, indexCount, out xnaPrim, out numPrims);

            _device.DrawIndexedPrimitives(xnaPrim, baseVertex, 0, indexCount, startIndex, numPrims);
        }

        public override void DrawIndexedInstanced(PrimitiveType primType, int indexCountPerInstance, int startIndex, int baseVertex, int instanceCount) {
            XFG.PrimitiveType xnaPrim;
            int numPrims;
            ComputeXNADrawCall(primType, indexCountPerInstance, out xnaPrim, out numPrims);

            _device.DrawInstancedPrimitives(xnaPrim, baseVertex, 0, indexCountPerInstance, startIndex, numPrims, instanceCount);
        }

        private void ComputeXNADrawCall(PrimitiveType primType, int indexCount, out XFG.PrimitiveType xnaPrim, out int numPrimitives) {
            switch(primType) {
                case PrimitiveType.TriangleList:
                    xnaPrim = XFG.PrimitiveType.TriangleList;
                    numPrimitives = indexCount / 3;
                    break;
                case PrimitiveType.TriangleStrip:
                    xnaPrim = XFG.PrimitiveType.TriangleStrip;
                    numPrimitives = indexCount - 2;
                    break;
                case PrimitiveType.LineStrip:
                    xnaPrim = XFG.PrimitiveType.LineStrip;
                    numPrimitives = indexCount - 1;
                    break;
                case PrimitiveType.LineList:
                    xnaPrim = XFG.PrimitiveType.LineList;
                    numPrimitives = indexCount / 2;
                    break;
                default:
                    throw new InvalidOperationException("Cannot convert XNA draw call parameters.");
            }
        }

        protected override void ApplyConcreteState(RenderState state) {
            //Silently fail
            if(state == null) {
                return;
            }

            //If we haven't been bound, automatically do it now
            if(!state.IsBound) {
                state.BindRenderState();
            }

            switch(state.StateType) {
                case RenderStateType.BlendState:
                    BlendState bs = state as BlendState;

                    //Set the XNA blend state
                    _device.BlendState = ((XNABlendStateImplementation) bs.Implementation).XNABlendstate;
                    return;
                case RenderStateType.DepthStencilState:
                    DepthStencilState dss = state as DepthStencilState;

                    //Set the XNA depth stencil state
                    _device.DepthStencilState = ((XNADepthStencilStateImplementation) dss.Implementation).XNADepthStencilState;
                    return;
                case RenderStateType.RasterizerState:
                    RasterizerState rs = state as RasterizerState;

                    //Set the XNA rasterizer state
                    _device.RasterizerState = ((XNARasterizerStateImplementation) rs.Implementation).XNARasterizerState;
                    return;
            }
        }

        protected override void Dispose(bool disposing) {
            if(!base.IsDisposed) {
                if(disposing) {
                    _device.Dispose();
                    _dummyForm.Dispose();
                }
                base.Dispose(disposing);
            }
        }

        public override RenderTargetBinding[] GetRenderTargets() {
            throw new NotImplementedException();
        }

        public override VertexBufferBinding[] GetVertexBuffers() {
            throw new NotImplementedException();
        }

        public override IndexBuffer GetIndexBuffer() {
            throw new NotImplementedException();
        }
    }
}
